package com.example.cls;

import com.sun.istack.internal.NotNull;

import java.io.Serializable;
import java.lang.annotation.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * @author zhangs
 * @version v1.0
 * @decription Class测试
 * @date 2021/4/5 23:38
 **/
public class ClsDemo {
    public static void main(String[] args) throws NoSuchMethodException {
        methodForGetClass();
        methodForClassMethod();
        getInstance();
        getCls();
    }

    static class Parent implements Serializable {

    }

    // 自定义注解：运行时，方法、方法参数、类
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.CONSTRUCTOR
            , ElementType.METHOD, ElementType.PARAMETER})
    public @interface An {
    }

    @Deprecated
    @An
    static class Demo extends Parent implements Serializable, Cloneable {
        @An
        private Demo(int one, @An String name) {
            System.out.println("构造方法被执行!");
        }

        static {
            System.out.println("静态代码块被执行!");
        }
    }

    /**
     * 获取Class对象的方法
     */
    public static void methodForGetClass() {
        // 1. 通过 类名.class 获取，支持基本类型和复杂对象
        // 此方法获取的Class对象不执行静态代码块和构造代码块
        // 即通过 类名.class 方法获取的Class对象不会初始化，因为不是主动使用
        Class<?> cb = boolean.class;
        Class<?> cs = String.class;
        Class<Demo> demoClass = Demo.class;

        // 2.通过Class.forName()来获取，参数为类的全限定名
        // 不支持基本类型
        // 会执行类的静态代码块，但不会执行动态构造块
        // 用于加载内部存在静态代码块的类，如注册数据库驱动
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("Driver类不存在！");
        }

        // 3.通过实例获取其Class对象
        Class<? extends String> clazz = "世界".getClass();

        // 4.基本类型和void类型的包装类支持TYPE字段获取
        Class<Double> type = Double.TYPE;
    }

    /**
     * Class 类的常用方法
     */
    static void methodForClassMethod() {
        Class<Demo> clazz = Demo.class;

        // 获取所在包
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage.getName()); // 打印包名

        // 获取类的访问修饰符和类名
        System.out.println("clazz.getName():\n\t" + clazz.getName());
        System.out.println("clazz.getCanonicalName():\n\t" + clazz.getCanonicalName());
        System.out.println("clazz.getSimpleName():\n\t" + clazz.getSimpleName());
        System.out.println("clazz.getTypeName():\n\t" + clazz.getTypeName());

        // 获取父类
        Class<? super Demo> superclass = clazz.getSuperclass();
        System.out.println("父类：" + superclass.getCanonicalName());

        // 获取接口
        Class<?>[] interfaces = clazz.getInterfaces();
        System.out.println("所有的接口：");
        for (Class<?> inter : interfaces) {
            System.out.println("\t" + inter.getCanonicalName());
        }

        // 判断是不是局部类
        boolean localClass = clazz.isLocalClass();
        System.out.println("Demo是局部类：" + localClass);

        // 判断是不是成员内部类
        boolean memberClass = clazz.isMemberClass();
        System.out.println("Demo是成员内部类：" + memberClass);

        // 获取类上的注解
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation.annotationType());
        }
    }

    /**
     * 返回Class类对应实体类的方法
     */
    static void getCls() {
        Class<Student> clazz = Student.class;

        // 获取父类
        Class<? super Student> superclass = clazz.getSuperclass();
        System.out.println("父类：" + superclass.getCanonicalName());

        // 获取类的public内部类，以及从父类继承的内部类
        Class<?>[] classes = clazz.getClasses();
        System.out.println("public 内部类：");
        for (Class<?> aClass : classes) {
            System.out.println("\t" + aClass.getCanonicalName());
        }

        // 获取类中定义的公共、私有、保护的内部类
        Class<?>[] declaredClasses = clazz.getDeclaredClasses();
        System.out.println("所有内部类：");
        for (Class<?> aClass : declaredClasses) {
            System.out.println("\t" + aClass.getCanonicalName());
        }
    }

    // 通过构造方法获取对象
    static void getInstance() throws NoSuchMethodException {
        Class<Demo> clazz = Demo.class;
        // 获取构造方法
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            // 获取构造方法的访问修饰符
            int modifiers = constructor.getModifiers();
            System.out.println("访问修饰符：" + Modifier.toString(modifiers));

            // 获取构造方法的参数
            int pc = constructor.getParameterCount();
            if (pc > 0) {
                Class<?>[] parameterTypes = constructor.getParameterTypes();
                for (Class<?> parameterType : parameterTypes) {
                    String typeName = parameterType.getTypeName();
                    System.out.println("参数类型：" + typeName);
                }
            }
            // 获取构造方法注解参数
            Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();

            for (Annotation[] parameterAnnotation : parameterAnnotations) {
                for (Annotation annotation : parameterAnnotation) {
                    System.out.println(annotation.toString());
                }
            }
        }
        // 实例化
        try {
            Constructor<Demo> dc = clazz.getDeclaredConstructor(int.class, String.class);
            // 绕过权限
            dc.setAccessible(true);
            Demo instance = dc.newInstance(23, "世界");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
